/* 
 * File:   MPIMaster.cpp
 * Author: mpienv
 * 
 * Created on July 27, 2011, 4:55 PM
 */

#include <stdio.h>

#include "MPIMaster.h"

MPIMaster::MPIMaster(int size, MGrasp *mgrasp, int maxIter, int maxEvals) {
    this->size = size;
    this->mgrasp = mgrasp;
    this->he = mgrasp->getHe();
    this->maxIter = maxIter;
    this->n = mgrasp->getN();
    this->func = mgrasp->getFunc();
    this->indexGap = 0;
    this->maxEvals = maxEvals;

    this->currentIter = 0;
    this->slaves_iters = new int[size];
    this->slave_current_round = new int[size];
    this->slaves_fn_evals = new int[size];
    this->best = new double[n];
    this->bestIter = new double[n];
    this->fBest = std::numeric_limits<double>::max();
    this->fBestIter = std::numeric_limits<double>::max();

    this->gaps = new double[7];
    this->evals = new int[7];

    evals[0] = 100;
    evals[1] = 500;
    evals[2] = 1000;
    evals[3] = 5000;
    evals[4] = 10000;
    evals[5] = 20000;
    evals[6] = 50000;

    for (int i = 0; i < size; i++) {
        slaves_fn_evals[i] = 0;
        slaves_iters[i] = 0;
        slave_current_round[i] = 0;
    }

    for (int i = 0; i < 7; i++) {
        gaps[i] = 0.0;
    }
}

MPIMaster::MPIMaster(const MPIMaster& orig) {
}

MPIMaster::~MPIMaster() {
    delete []slaves_iters;
    delete []slaves_fn_evals;
    delete []best;
    delete []bestIter;
    delete []gaps;
    delete []evals;
}

//Inicia o ambiente...

void MPIMaster::initEnv() {

}

bool MPIMaster::verifyGap(double fX, double *tempX, int totalEvals) {
    if ((maxEvals <= totalEvals) && (func->getFnEvals() > evals[indexGap])) { //Entender essa linha...

        gaps[indexGap] = func->getGap();
        indexGap++;
    }

    if (indexGap == 7) {
        return true;
    }

    return false;
}

int MPIMaster::totalSlavesEvals() {
    int total = 0;

    for (int i = 0; i < size; i++) {
        total += slaves_fn_evals[i];
    }

    return total;
}

int MPIMaster::getResultTotalEvals() {
    return this->result_total_evals;
}

double MPIMaster::getResultGAP() {
    return this->result_gap;
}

bool MPIMaster::mustFinish(double fX, double *tempX, int totalEvals, double fBest) {
    //    if (verifyGap(fX, tempX, totalEvals)) {
    //        printf("Mestre - StepGap = 7 (FNEvals = 50000)\n");
    //        return true;
    //    }

    if (mgrasp->getFunc()->isNearOptimum(fBest)) {
        printf("Mestre - Achou o OTIMO = %0.8lf\n", fBest);
        return true;
    }

    //    if (currentIter >= maxIter) {
    //        printf("Mestre - MaxIter foi alcançado\n");
    //        return true;
    //    }

    //    if (maxEvals < totalEvals) {
    //        printf("Mestre - MaxEvals alcançado (%d)...\n", totalEvals);
    //        return true;
    //    }

    return false;
}

void MPIMaster::updateIfBest(double fTempX, double *tempX, double h, bool onlyGlobal) {



    if (!onlyGlobal) {
        if (Util::equals(fTempX, this->fBestIter)) {
            if (h < hBestIter) {
                hBestIter = h;
            }
        }

        if (fTempX < this->fBestIter) {

            if (MPIBasics::DEBUG_MASTER) {
                printf("Mestre - Local Nova solucao  (%0.8lf)\n", fTempX);
            }

            this->fBestIter = fTempX;
            this->hBestIter = h;
            for (int i = 0; i < n; i++) {
                bestIter[i] = tempX[i];
            }
            //Util::copy(tempX, bestIter, n);


        }
    }

    if (fTempX < this->fBest) { //Se for melhor atualiza a solução

        if (MPIBasics::DEBUG_MASTER) {
            printf("Mestre - Global Nova solucao (%0.8lf)\n", fTempX);
        }


        this->fBest = fTempX;
        this->hBest = h;
        func->setBestValue(fBest);
        for (int i = 0; i < n; i++) {
            best[i] = tempX[i];
        }
        //        for(int i = 0; i < this->n; i++){
        //            best[i] = tempX[i];
        //            
        //        }
    }
}

Funcao* MPIMaster::startDaemon() {

    if (MPIBasics::DEBUG_MASTER)
        printf("Mestre - Inicializado (he = %lf)..\n", he);

    long initTime = time(NULL);

    long finalTime = initTime + 3 * 60 + 30; //Para em 3 minutos e meio...

    double *tempX = new double[n];
    bool finalizeAll = false;
    int deadProcess = 0;
    int totalEvals = 0;
    int currentRound = 0;

    while (deadProcess != size - 1) {

        currentIter++;

        MPI_Status status;
        MPI_Status hStatus;
        MPI_Status eStatus;
        int dest, totalEvalsSlave;
        double h;
        bool newRound = false;
        MPIBasics::receiveSolution(tempX, n, MPI_ANY_SOURCE, MPI_ANY_TAG, &status);
        MPIBasics::receiveDouble(&h, 1, status.MPI_SOURCE, MPIBasics::TAG_CURRENT_H, &hStatus);
        MPIBasics::receiveInteger(&totalEvalsSlave, 1, status.MPI_SOURCE, MPIBasics::TAG_FNEVALS_TOTAL, &eStatus);

        slaves_fn_evals[eStatus.MPI_SOURCE] = totalEvalsSlave;
        
        
        result_total_evals = totalSlavesEvals();
        dest = status.MPI_SOURCE;

        slaves_iters[dest]++;

        double fTempX = this->func->calc(tempX);

        if (MPIBasics::DEBUG_MASTER) {            
            printf("Mestre (Rodada %d) - New Solution from (%d) (Value=%0.8lf) (H = %lf)\n", currentRound, status.MPI_SOURCE, fTempX, h);
        }

        bool onlyGlobal = false;
        if (slave_current_round[dest] != currentRound) {
            slave_current_round[dest] = currentRound;
            onlyGlobal = true;

            if (MPIBasics::DEBUG_MASTER) {
                printf("Mestre - Atualizando escravo %d\n", dest);
            }

        }

        updateIfBest(fTempX, tempX, h, onlyGlobal);

        finalizeAll = mustFinish(fTempX, tempX, totalEvals, fBest);
        if(result_total_evals > maxEvals){
            printf("Max Evals encontrada %d - %d\n", result_total_evals, maxEvals);
            finalizeAll = true;
        }
//        if (time(NULL) > finalTime) {
//            finalizeAll = true;
//        }

        if (onlyGlobal) {
            newRound = true;
        }

        //Inicia outra rodada...
        if (hBestIter / 2.0 < he) {
            currentRound++;
            slave_current_round[dest]++;
            fBestIter = std::numeric_limits<double>::max(); 
            hBestIter = std::numeric_limits<double>::max();
            newRound = true;
        }

        if (finalizeAll) {
            int value = true;
            int evals;
            MPI_Status evalsStatus;
            MPIBasics::sendInteger(&value, 1, dest, MPIBasics::TAG_FINALIZE_SLAVE);
            MPIBasics::receiveInteger(&evals, 1, dest, MPIBasics::TAG_FNEVALS_TOTAL, &evalsStatus);
            slaves_fn_evals[dest] = evals;
            deadProcess++;
        } else if (newRound) {
            int value = true;
            printf("Mestre - Iniciando uma nova rodada no escravo %d\n", dest);
            MPIBasics::sendInteger(&value, 1, dest, MPIBasics::TAG_RESTART_GRASP);
        } else {

            int value = true;
            MPIBasics::sendInteger(&value, 1, dest, MPIBasics::TAG_WAIT_NEW_SOLUTION);

            if (MPIBasics::DEBUG_MASTER) {
                printf("Mestre - Enviando solução (%0.8lf) h (%lf)\n", func->calc(bestIter), hBestIter);
            }

            MPIBasics::sendSolution(bestIter, n, dest, MPIBasics::TAG_BEST_SOLUTION);
            MPIBasics::sendDouble(&hBestIter, 1, dest, MPIBasics::TAG_BEST_H);
        }

    }


    for (int i = 0; i < size; i++) {
        printf("Mestre - (Slave[%d] - Total Iter (%d) - Total Evals (%d))\n", i, slaves_iters[i], slaves_fn_evals[i]);
    }

    result_total_evals = totalSlavesEvals();
    result_gap = func->getGap();
    
    printf("Mestre - TOTAL DE FNEVALS: %d\n", result_total_evals);
    printf("Mestre - FUNCAO OBJETIVO: %0.8lf\n", fBest);
    printf("Mestre - GAP: %0.8lf\n", func->getGap());

    delete [] tempX;

    if (MPIBasics::DEBUG_MASTER) {
        printf("Mestre - finalizado\n");
    }

    func->setFnEvals(totalEvals);

    return func;
}
